ALV-Grid um Sortierfunktion erweitern (Vererbung)
Objektorientierte Programmierung ist häufig immer noch ein rotes Tuch für viele. Man weiß zwar, wie Methoden aufgerufen werden und dass ein Objekt mit CREATE OBJECT oder NEW erzeugt werden muss aber die Designprinzipien sind irgendwie unklar. Und SAP-Klassen sind eh unantastbar.
In diesem Artikel möchte ich dir eine Möglichkeit vorstellen, wie du den SAP-Standard mit Standardmitteln, nämlich mit Hilfe der Vererbung, erweitern kannst.
Enjoy und Bedienung
Trotz der GUI-Elemente, die unter dem Schlagwort ENJOY eingeführt wurden, sind viele Elemente immer noch nicht wirklich benutzerfreundlich. Einiges kann man ändern, anderes nicht. Eine Möglichkeit um mit Hilfe der SAP-Standardcontrols ein neues Look & Feel zu erzeugen, habe ich in diesem Beitrag gezeigt: Moderne UI mit altem SAPGUI und ALV-Grid
Hier habe ich das Standard-ALV-Grid verwendet, um eine neue Funktionalität zu erzeugen. Dies ist allerdings ein eigenständiges Objekt und erweitert nicht die Standardfunktionalität des ALV-Grids.
Wie das Überschreiben von geschützten Methoden generell funktioniert, habe ich hier beschrieben: Geschützte Methoden nutzen
Dieser Artikel soll zeigen, dass es sich eventuell lohnt, auch über andere Erweiterungen von SAP-Standardfunktionalitäten nachzudenken. Es gibt Funktionalitäten, die eventuell in jedem ALV-Grid hilfreich wären. Zum Beispiel das einfache Umsortieren von Einträgen.
Umsortierung mittels Drag & Drop
Eine Möglichkeit ist die Sortierung mittels Drag&Drop im ALV-Feldkatalog:
Die Bedienung ist hier zwar auch gewöhnungsbedürftig, denn ein Eintrag, der umsortiert werden soll, muss erst mit einem Klick markiert und kann dann erst mittels Drag & Drop an eine andere Stelle verschoben werden, aber immerhin.
Umsortierung mit Funktionstasten
Eine andere Möglichkeit wäre das Verschieben von Einträgen mit Funktionstasten. Folgender Screenshot ist aus dem unten stehenden Demo-Programm. Das ALV-Grid wurde um die Funktionstasten “Sort Up” und “Sort Down” erweitert.
Wie das im Einzelnen geht, erkläre ich gleich.
Redefinition
eine wirklich starke Waffe des objektorientierten Sprachumfangs ist die Vererbung. Sofern die anzupassende Klasse nicht als “Final” definiert wurde, können geschützte und öffentliche Methoden redefiniert werden. Leider wird bei Anlage einer Klasse das Kennzeichen “Final” vorbelegt, so dass der Programmierer dieses aktiv entfernen muss. Ist dieses Kennzeichen gesetzt, funktioniert die Ableitung bzw. Vererbung nicht.
Die Klasse CL_GUI_ALV_GRID allerdings darf vererbt werden, denn sie ist nicht als final gekennzeichnet. Nichts desto Trotz muss bei Klassen sehr genau definiert werden, welche Methoden vererbt werden dürfen und welche nicht.
Funktion “Umsortieren”
Die neue Funktion nenne ich “Umsortieren”, denn es ist keine Sortieren-Funktion, die man aus dem Standard kennt. Um Einträge umsortieren zu können, wird in der Regel im SAP mit einem Sortierfeld gearbeitet, das der Anwender manuell pflegen muss. Dazu wird häufig in Zehnerschritten gearbeitet, um später Einträge einfügen zu können. Einträge in dieser Form in eine andere Reihenfolge zu bringen ist in der Regel sehr mühselig.
Die Idee ist, dass der Anwender einen Eintrag markieren kann und diesem mit den Funktionstasten “Sort Up” und Sort Down” in der Liste hoch und runter verschieben kann. Ein Feld, in der die aktuelle Reihenfolge festgehalten wird, brauchen wir natürlich trotzdem.
Vererbung
Ich möchte die Klasse CL_GUI_ALV_GRID also für meine Zwecke missbrauchen und muss sie deswegen ableiten bzw. vererben. Dazu lege ich in der Transaktion SE80 oder SE24 eine neue Klasse an: ZCL_GUI_ALV_GRID_SORT und gebe als Oberklasse zu zu beerbende Klasse CL_GUI_ALV_GRID an:
Sortierfeld
Der Programmierer muss angeben können, welches Feld für die Sortierung der Einträge verwendet werden soll. In diesem Feld wird dann die automatische Nummerierung anhand der Reihenfolge gesetzt. Ich lege dafür die Methode SET_SORT_FIELD an mit dem Übergabeparameter FIELDNAME.
Diese Methode muss vor SET_TABLE_FOR_FIRST_DISPLAY aufgerufen werden, damit die Umsortierfunktionalität von Anfang an zur Verfügung steht.
Das Sortierfeld merke ich mir im Attribut MV_SORT_FIELD.
Ereignis TOOLBAR
Da ich das Ereignis TOOLBAR nutzen möchte, um die neuen Funktionstasten einzubauen, muss ich es für meine abgeleitete Klasse registrieren:
SET HANDLER on_toolbar FOR me.
Zusätzlich benötige ich eine Methode, die beim Auslösen des Ereignisses angesprungen wird: ON_TOOLBAR. Diese Methode muss als Ereignisbehandler für das Ereignis TOOLBAR definiert werden:
Der Methode stehen nun theoretisch alle Parameter des Ereignisses zur Verfügung. Allerdings müssen diese manuell übernommen werden. Die Drucktaste “Ereignisparameter” in der Sicht “Parameter” erledigt das für mich:
In der Methode füge ich die Drucktasten SORT_UP und SORT_DOWN der Toolbar hinzu.
METHOD on_toolbar. check mv_sort_field is NOT INITIAL. APPEND VALUE #( function = 'Sort_down' icon = icon_next_page quickinfo = space butn_type = if_sat_ui_button_types=>normal disabled = space text = 'Sort down' checked = space ) TO e_object->mt_toolbar. APPEND VALUE #( function = 'Sort_up' icon = icon_previous_page quickinfo = space butn_type = if_sat_ui_button_types=>normal disabled = space text = 'Sort up' checked = space ) TO e_object->mt_toolbar. ENDMETHOD.
Redefinition DISPATCH
Um intern auf die Drucktasten reagieren zu können, muss ich die Methode DISPATCH redefinieren und meine Drucktasten SORT_UP und SORT_DOWN für das Ereignis TOOLBAR_BUTTON_CLICK abfangen.
In allen anderen Fällen muss die Methode DISPATCH der abgeleiteten Klasse aufgerufen werden (SUPER->DISPATCH).
Im Falle des Ereignisses TOOLBAR_BUTTON_CLICK muss ich mir noch die Ereignisparameter besorgen in denen der Funktionscode der Drucktaste steht (Methode GET_EVENT_PARAMETER).
Nun gilt es noch, folgendes zu tun:
- Abfrage auf die Funktionscodes SORT_UP und SORT_DOWN
- Zugriff auf die Datentabelle erhalten
- Ermitteln der aktuellen Cursorposition
- Umsortieren des Eintrags
- Neunummerierung
- Cursor auf die umsortierte Zeile setzen
- Anzeige aktualisieren
METHOD dispatch. DATA action TYPE string. CASE eventid. WHEN evt_toolbar_button_click. CALL METHOD get_event_parameter EXPORTING parameter_id = 0 queue_only = space IMPORTING parameter = action. CALL METHOD cl_gui_cfw=>flush. CASE action. WHEN 'Sort_up'. CALL METHOD get_current_cell IMPORTING es_row_id = DATA(ls_row) es_row_no = DATA(ls_row_no). FIELD-SYMBOLS <outtab> TYPE table. ASSIGN mt_outtab->* TO <outtab>. IF ls_row-index > 1. READ TABLE <outtab> ASSIGNING FIELD-SYMBOL(<outline>) INDEX ls_row-index. DATA(indx) = ls_row-index - 1. ASSIGN COMPONENT 2 OF STRUCTURE <outline> TO FIELD-SYMBOL(<value>). IF sy-subrc = 0. INSERT <outline> INTO <outtab> INDEX indx. indx = indx + 2. DELETE <outtab> INDEX indx. indx = indx - 2. LOOP AT <outtab> ASSIGNING <outline>. ASSIGN COMPONENT mv_sort_field OF STRUCTURE <outline> TO <value>. <value> = sy-tabix. ENDLOOP. refresh_table_display( is_stable = VALUE #( col = abap_true row = abap_true ) i_soft_refresh = abap_true ). set_selected_rows( it_row_no = VALUE #( ( row_id = indx ) ) ). ENDIF. ENDIF. EXIT. WHEN 'Sort_down'. CALL METHOD get_current_cell IMPORTING es_row_id = ls_row es_row_no = ls_row_no. ASSIGN mt_outtab->* TO <outtab>. IF ls_row-index < lines( <outtab> ). READ TABLE <outtab> ASSIGNING <outline> INDEX ls_row-index. indx = ls_row-index + 2. ASSIGN COMPONENT 2 OF STRUCTURE <outline> TO <value>. IF sy-subrc = 0. INSERT <outline> INTO <outtab> INDEX indx. indx = indx - 2. DELETE <outtab> INDEX indx. indx = indx + 1. LOOP AT <outtab> ASSIGNING <outline>. ASSIGN COMPONENT mv_sort_field OF STRUCTURE <outline> TO <value>. <value> = sy-tabix. ENDLOOP. refresh_table_display( is_stable = VALUE #( col = abap_true row = abap_true ) i_soft_refresh = abap_true ). set_selected_rows( it_row_no = VALUE #( ( row_id = indx ) ) ). ENDIF. ENDIF. EXIT. ENDCASE. ENDCASE. super->dispatch( EXPORTING cargo = cargo eventid = eventid is_shellevent = is_shellevent is_systemdispatch = is_systemdispatch EXCEPTIONS cntl_error = 1 OTHERS = 2 ). ENDMETHOD.
Testprogramm
In folgendem Testprogramm kannst du die Verwendung des neuen Standards sehen. Du siehst, dass trotz meines Eingriffs in die Toolbar weiterhin Drucktasten hinzugefügt werden können:
REPORT zz_alv_grid_sort. DATA gs_data TYPE vbak. SELECT-OPTIONS s_vbeln FOR gs_data-vbeln. CLASS main DEFINITION. PUBLIC SECTION. TYPES ty_data TYPE vbak. TYPES ty_data_t TYPE STANDARD TABLE OF ty_data WITH DEFAULT KEY. DATA ms_data TYPE ty_data. DATA mt_data TYPE ty_data_t. DATA mr_grid TYPE REF TO zcl_gui_alv_grid_sort. METHODS start. PROTECTED SECTION. METHODS selection. METHODS display. METHODS handle_toolbar FOR EVENT toolbar OF cl_gui_alv_grid IMPORTING e_object. METHODS handle_user_command FOR EVENT user_command OF cl_gui_alv_grid IMPORTING e_ucomm sender. ENDCLASS. CLASS main IMPLEMENTATION. METHOD handle_user_command. DATA lt_rows TYPE lvc_t_row. DATA ls_row TYPE lvc_s_row. DATA ls_data TYPE ty_data. CASE e_ucomm. WHEN 'USER01'. sender->get_selected_rows( IMPORTING et_index_rows = lt_rows ). LOOP AT lt_rows INTO ls_row. READ TABLE mt_data INTO ls_data INDEX ls_row-index. IF sy-subrc = 0. MESSAGE i000(oo) WITH 'Usercommand 01: Beleg' ls_data-vbeln. ENDIF. ENDLOOP. ENDCASE. ENDMETHOD. METHOD handle_toolbar. DATA: ls_toolbar TYPE stb_button. *** Trenner CLEAR ls_toolbar. MOVE 3 TO ls_toolbar-butn_type. APPEND ls_toolbar TO e_object->mt_toolbar. *** Icon “Test” CLEAR ls_toolbar. MOVE icon_generate TO ls_toolbar-icon. MOVE 'USER01' TO ls_toolbar-function. MOVE 'User 01' TO ls_toolbar-quickinfo. MOVE 'Userbutton 01' TO ls_toolbar-text. APPEND ls_toolbar TO e_object->mt_toolbar. ENDMETHOD. METHOD start. selection( ). display( ). ENDMETHOD. METHOD selection. SELECT * FROM vbak INTO TABLE mt_data UP TO 10 ROWS. ENDMETHOD. METHOD display. WRITE 'DUMMY'. CREATE OBJECT mr_grid EXPORTING i_parent = cl_gui_container=>screen0 i_appl_events = space. mr_grid->set_sort_field( 'ERNAM' ). SET HANDLER handle_toolbar FOR mr_grid. SET HANDLER handle_user_command FOR mr_grid. DATA lv_structure_name TYPE dd02l-tabname VALUE 'VBAK'. DATA ls_variant TYPE disvariant. DATA lv_save TYPE char01 VALUE 'U'. DATA lv_default TYPE char01 VALUE abap_true. DATA ls_layout TYPE lvc_s_layo. ls_layout-sel_mode = 'A'. ls_layout-grid_title = 'Titel'. mr_grid->set_table_for_first_display( EXPORTING i_structure_name = lv_structure_name is_variant = ls_variant i_save = lv_save i_default = lv_default is_layout = ls_layout CHANGING it_outtab = mt_data ). ENDMETHOD. ENDCLASS. START-OF-SELECTION. NEW main( )->start( ).
Ergebnis
Du kannst nun den Cursor auf einen Eintrag stellen und durch Klicken auf “Sort Up” oder “Sort Down” den Eintrag umsortieren. Die Sortierung wird in dem Feld “ERNAM – Angelegt von” vorgehalten.
Fazit
Die Änderung von SAP-Standardfunktionen ist möglich. Allerdings ist das erstens nicht immer so einfach, wie in diesem Artikel beschrieben. In der Regel muss man genau und langwierig debuggen und prüfen, wo welche Methoden verwendet werden können. Zudem müssen die Funktionen natürlich ausgiebig getestet werden. Immerhin sollen sie genau wie die Standardfunktionalität zuverlässig funktionieren.
Des Weiteren sollten Funktionen, die wirklich in einer Vielzahl von eigenen Programmierungen eingesetzt werden sauber ausprogrammiert werden. In dem hier vorgestellten Beispiel sollte zum Beispiel sichergestellt werden, dass das Feld mit der Sortierung auch wirklich im Feldkatalog vorhanden ist. Die Benutzereigene Sortierung muss irgendwie berücksichtigt werden.
Zudem sollte es natürlich möglich sein, auch mehrere Zeilen zu markieren und diese en bloc zu verschieben. Das hängt aber wiederum von der Programmierung ab, ob wirklich mehrere Zeilen markiert werden dürfen oder nicht.
Ebenso wäre die Eingangs erwähnte Sortierung mittels Drag & Drop sinnvoll. Diese könnte dann allerdings einer anderen vom Programmierer erstellten Drag & Drop Funktionalität in die Quere kommen.
Allerdings lohnt es sich, hier Aufwand zu investieren, denn die erweiterte Funktionalität kann eventuell viele separate Programmierungen überflüssig machen oder vorhandene Programmierungen auf einfache Weise benutzerfreundlicher machen.
- 7. December: Excel Racing Simulation – Root Vole Race - 7. Dezember 2024
- 5. December: ABAPConf - 5. Dezember 2024
- 4. December: Only a lazy developer is a good developer - 4. Dezember 2024